import os
import sys
import subprocess
import configparser
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import datetime
import platform
import logging
from pathlib import Path

class MySQLManager:
    def __init__(self, config_path="mysql_manager_config.ini"):
        self.config_path = config_path
        self.config = configparser.ConfigParser()
        self.mysql_path = ""
        self.mysql_bin_path = ""
        self.mysql_data_path = ""
        self.mysql_service_name = "MySQL80"  # 默认服务名称
        self.load_config()
        
        # 确保日志目录存在
        log_dir = Path("logs")
        log_dir.mkdir(exist_ok=True)
        
        # 配置日志
        logging.basicConfig(
            filename=log_dir / f"mysql_manager_{datetime.datetime.now().strftime('%Y%m%d')}.log",
            level=logging.INFO,
            format="%(asctime)s - %(levelname)s - %(message)s"
        )
        
        # 检查是否为 Windows 平台
        if platform.system() != "Windows":
            messagebox.showerror("错误", "此工具仅支持 Windows 平台")
            sys.exit(1)
            
        # 存储进程信息
        self.mysql_process = None

    def load_config(self):
        """加载配置文件"""
        if os.path.exists(self.config_path):
            self.config.read(self.config_path)
            if "MySQL" in self.config:
                self.mysql_path = self.config["MySQL"].get("path", "")
                self.mysql_service_name = self.config["MySQL"].get("service_name", "MySQL80")
                self.update_paths()
    
    def save_config(self):
        """保存配置文件"""
        if not self.config.has_section("MySQL"):
            self.config.add_section("MySQL")
        self.config["MySQL"]["path"] = self.mysql_path
        self.config["MySQL"]["service_name"] = self.mysql_service_name
        with open(self.config_path, "w") as f:
            self.config.write(f)
    
    def update_paths(self):
        """更新 MySQL 相关路径"""
        if self.mysql_path:
            self.mysql_bin_path = os.path.join(self.mysql_path, "bin")
            self.mysql_data_path = os.path.join(self.mysql_path, "data")
    
    def is_mysql_installed(self):
        """检查 MySQL 是否已安装"""
        if not self.mysql_path:
            return False
        return os.path.exists(self.mysql_bin_path) and os.listdir(self.mysql_bin_path)
    
    def is_service_installed(self):
        """检查 MySQL 服务是否已安装"""
        try:
            result = subprocess.run(
                f'sc query "{self.mysql_service_name}"',
                shell=True,
                capture_output=True,
                text=True
            )
            return "FAILED" not in result.stdout
        except Exception as e:
            logging.error(f"检查服务是否安装时出错: {e}")
            return False
    
    def get_service_status(self):
        """获取 MySQL 服务状态"""
        if self.mysql_process and self.mysql_process.poll() is None:
            return "免安装模式运行中"
            
        if not self.is_service_installed():
            return "未安装"
        
        try:
            result = subprocess.run(
                f'sc query "{self.mysql_service_name}"',
                shell=True,
                capture_output=True,
                text=True
            )
            for line in result.stdout.splitlines():
                if "STATE" in line and "RUNNING" in line:
                    return "服务模式运行中"
            return "已停止"
        except Exception as e:
            logging.error(f"获取服务状态时出错: {e}")
            return "未知状态"
    
    def start_service(self):
        """启动 MySQL 服务"""
        if not self.is_service_installed():
            return False, "MySQL 服务未安装，请使用'免安装启动'功能"
        
        try:
            result = subprocess.run(
                f'net start "{self.mysql_service_name}"',
                shell=True,
                capture_output=True,
                text=True
            )
            if "已启动" in result.stdout or "The service is starting" in result.stdout:
                return True, "MySQL 服务已启动"
            else:
                return False, f"启动服务失败: {result.stderr}"
        except Exception as e:
            logging.error(f"启动服务时出错: {e}")
            return False, f"启动服务时出错: {e}"
    
    def stop_service(self):
        """停止 MySQL 服务"""
        if not self.is_service_installed():
            if self.mysql_process and self.mysql_process.poll() is None:
                return self.stop_standalone()
            return False, "MySQL 服务未安装"
        
        try:
            result = subprocess.run(
                f'net stop "{self.mysql_service_name}"',
                shell=True,
                capture_output=True,
                text=True
            )
            if "已停止" in result.stdout or "The service is stopping" in result.stdout:
                return True, "MySQL 服务已停止"
            else:
                return False, f"停止服务失败: {result.stderr}"
        except Exception as e:
            logging.error(f"停止服务时出错: {e}")
            return False, f"停止服务时出错: {e}"
    
    def install_service(self):
        """安装 MySQL 服务"""
        if not self.is_mysql_installed():
            return False, "请先配置 MySQL 安装路径"
        
        if self.is_service_installed():
            return False, "MySQL 服务已安装"
        
        try:
            # 创建 my.ini 配置文件
            my_ini_path = os.path.join(self.mysql_path, "my.ini")
            if not os.path.exists(my_ini_path):
                with open(my_ini_path, "w") as f:
                    f.write(f"""[mysqld]
basedir={self.mysql_path}
datadir={self.mysql_data_path}
port=3306
character-set-server=utf8mb4
default-storage-engine=InnoDB
""")
            
            # 初始化数据目录
            init_cmd = os.path.join(self.mysql_bin_path, "mysqld.exe")
            init_result = subprocess.run(
                f'"{init_cmd}" --initialize-insecure --console',
                shell=True,
                capture_output=True,
                text=True
            )
            if init_result.returncode != 0:
                return False, f"初始化数据目录失败: {init_result.stderr}"
            
            # 安装服务
            install_cmd = os.path.join(self.mysql_bin_path, "mysqld.exe")
            install_result = subprocess.run(
                f'"{install_cmd}" --install "{self.mysql_service_name}"',
                shell=True,
                capture_output=True,
                text=True
            )
            if install_result.returncode != 0:
                return False, f"安装服务失败: {install_result.stderr}"
            
            return True, "MySQL 服务已安装并初始化"
        except Exception as e:
            logging.error(f"安装服务时出错: {e}")
            return False, f"安装服务时出错: {e}"
    
    def uninstall_service(self):
        """卸载 MySQL 服务"""
        if not self.is_service_installed():
            return False, "MySQL 服务未安装"
        
        # 先停止服务
        self.stop_service()
        
        try:
            result = subprocess.run(
                f'"{os.path.join(self.mysql_bin_path, "mysqld.exe")}" --remove "{self.mysql_service_name}"',
                shell=True,
                capture_output=True,
                text=True
            )
            if "Service successfully removed" in result.stdout:
                return True, "MySQL 服务已卸载"
            else:
                return False, f"卸载服务失败: {result.stderr}"
        except Exception as e:
            logging.error(f"卸载服务时出错: {e}")
            return False, f"卸载服务时出错: {e}"
    
    def export_database(self, db_name, output_file):
        """导出数据库"""
        if not self.is_mysql_installed():
            return False, "请先配置 MySQL 安装路径"
        
        status = self.get_service_status()
        if "运行中" not in status:
            return False, "请先启动 MySQL 服务"
        
        try:
            mysqldump_cmd = os.path.join(self.mysql_bin_path, "mysqldump.exe")
            cmd = f'"{mysqldump_cmd}" -u root --all-databases > "{output_file}"'
            if db_name:
                cmd = f'"{mysqldump_cmd}" -u root {db_name} > "{output_file}"'
            
            # 使用 subprocess 执行导出命令
            result = subprocess.run(
                cmd,
                shell=True,
                capture_output=True,
                text=True,
                timeout=3600  # 设置超时时间为1小时
            )
            if result.returncode != 0:
                return False, f"导出数据库失败: {result.stderr}"
            
            return True, f"数据库已导出到 {output_file}"
        except Exception as e:
            logging.error(f"导出数据库时出错: {e}")
            return False, f"导出数据库时出错: {e}"
    
    def initialize_data_directory(self):
        """初始化数据目录（免安装模式）"""
        if not self.is_mysql_installed():
            return False, "请先配置 MySQL 安装路径"
            
        # 检查数据目录是否已存在
        if os.path.exists(self.mysql_data_path) and os.listdir(self.mysql_data_path):
            return True, "数据目录已存在"
            
        try:
            # 创建 my.ini 配置文件
            my_ini_path = os.path.join(self.mysql_path, "my.ini")
            if not os.path.exists(my_ini_path):
                with open(my_ini_path, "w") as f:
                    f.write(f"""[mysqld]
basedir={self.mysql_path}
datadir={self.mysql_data_path}
port=3306
character-set-server=utf8mb4
default-storage-engine=InnoDB
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
""")
            
            # 初始化数据目录
            init_cmd = os.path.join(self.mysql_bin_path, "mysqld.exe")
            init_result = subprocess.run(
                f'"{init_cmd}" --initialize-insecure --console',
                shell=True,
                capture_output=True,
                text=True
            )
            if init_result.returncode != 0:
                return False, f"初始化数据目录失败: {init_result.stderr}"
            
            return True, "数据目录已初始化"
        except Exception as e:
            logging.error(f"初始化数据目录时出错: {e}")
            return False, f"初始化数据目录时出错: {e}"
    
    def start_standalone(self):
        """以免安装模式启动 MySQL"""
        if not self.is_mysql_installed():
            return False, "请先配置 MySQL 安装路径"
            
        # 检查数据目录
        if not os.path.exists(self.mysql_data_path) or not os.listdir(self.mysql_data_path):
            result, message = self.initialize_data_directory()
            if not result:
                return result, message
                
        # 检查是否已在运行
        if self.mysql_process and self.mysql_process.poll() is None:
            return False, "MySQL 已在免安装模式下运行"
            
        try:
            mysqld_cmd = os.path.join(self.mysql_bin_path, "mysqld.exe")
            my_ini_path = os.path.join(self.mysql_path, "my.ini")
            
            # 启动 MySQL 服务器进程
            self.mysql_process = subprocess.Popen(
                [mysqld_cmd, "--defaults-file=" + my_ini_path],
                cwd=self.mysql_bin_path,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True
            )
            
            # 检查进程是否启动成功
            if self.mysql_process.poll() is not None:
                error_output = self.mysql_process.stdout.read() if self.mysql_process.stdout else "No output"
                return False, f"启动失败: {error_output}"
                
            return True, "MySQL 已在免安装模式下启动"
        except Exception as e:
            logging.error(f"免安装启动时出错: {e}")
            return False, f"免安装启动时出错: {e}"
    
    def stop_standalone(self):
        """停止免安装模式下的 MySQL"""
        if not self.mysql_process or self.mysql_process.poll() is not None:
            return False, "MySQL 未在免安装模式下运行"
            
        try:
            # 尝试正常关闭
            mysqladmin_cmd = os.path.join(self.mysql_bin_path, "mysqladmin.exe")
            subprocess.run(
                [mysqladmin_cmd, "-u", "root", "shutdown"],
                cwd=self.mysql_bin_path,
                capture_output=True,
                text=True
            )
            
            # 等待进程结束
            self.mysql_process.wait(timeout=10)
            
            return True, "MySQL 已在免安装模式下停止"
        except subprocess.TimeoutExpired:
            # 如果超时，强制终止
            self.mysql_process.terminate()
            self.mysql_process.wait(timeout=5)
            return True, "MySQL 已被强制停止"
        except Exception as e:
            logging.error(f"免安装停止时出错: {e}")
            return False, f"免安装停止时出错: {e}"

class MySQLManagerGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("MySQL 服务管理工具")
        self.root.geometry("800x600")
        self.root.minsize(600, 500)
        
        self.manager = MySQLManager()
        
        self.create_widgets()
        self.update_status()
    
    def create_widgets(self):
        """创建 GUI 组件"""
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 配置部分
        config_frame = ttk.LabelFrame(main_frame, text="MySQL 配置", padding="10")
        config_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(config_frame, text="安装路径:").grid(row=0, column=0, sticky=tk.W, pady=2)
        self.path_var = tk.StringVar(value=self.manager.mysql_path)
        self.path_entry = ttk.Entry(config_frame, textvariable=self.path_var, width=50)
        self.path_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=2, padx=5)
        
        browse_btn = ttk.Button(config_frame, text="浏览...", command=self.browse_path)
        browse_btn.grid(row=0, column=2, pady=2, padx=5)
        
        ttk.Label(config_frame, text="服务名称:").grid(row=1, column=0, sticky=tk.W, pady=2)
        self.service_var = tk.StringVar(value=self.manager.mysql_service_name)
        self.service_entry = ttk.Entry(config_frame, textvariable=self.service_var, width=20)
        self.service_entry.grid(row=1, column=1, sticky=tk.W, pady=2)
        
        save_config_btn = ttk.Button(config_frame, text="保存配置", command=self.save_config)
        save_config_btn.grid(row=1, column=2, pady=2, padx=5)
        
        config_frame.columnconfigure(1, weight=1)
        
        # 状态部分
        status_frame = ttk.LabelFrame(main_frame, text="服务状态", padding="10")
        status_frame.pack(fill=tk.X, pady=5)
        
        self.status_var = tk.StringVar(value="未检测到服务状态")
        ttk.Label(status_frame, textvariable=self.status_var).grid(row=0, column=0, sticky=tk.W)
        
        # 控制按钮
        control_frame = ttk.Frame(status_frame)
        control_frame.grid(row=0, column=1, sticky=tk.E)
        
        self.start_btn = ttk.Button(control_frame, text="启动服务", command=self.start_service)
        self.start_btn.pack(side=tk.LEFT, padx=5)
        
        self.stop_btn = ttk.Button(control_frame, text="停止服务", command=self.stop_service)
        self.stop_btn.pack(side=tk.LEFT, padx=5)
        
        # 操作按钮
        action_frame = ttk.Frame(main_frame, padding="10")
        action_frame.pack(fill=tk.X, pady=5)
        
        self.install_btn = ttk.Button(action_frame, text="安装并初始化服务", command=self.install_service)
        self.install_btn.pack(side=tk.LEFT, padx=5)
        
        self.uninstall_btn = ttk.Button(action_frame, text="卸载服务", command=self.uninstall_service)
        self.uninstall_btn.pack(side=tk.LEFT, padx=5)
        
        self.export_btn = ttk.Button(action_frame, text="导出数据库", command=self.export_database)
        self.export_btn.pack(side=tk.LEFT, padx=5)
        
        # 免安装模式按钮
        standalone_frame = ttk.LabelFrame(main_frame, text="免安装模式", padding="10")
        standalone_frame.pack(fill=tk.X, pady=5)
        
        self.start_standalone_btn = ttk.Button(standalone_frame, text="免安装启动", command=self.start_standalone)
        self.start_standalone_btn.pack(side=tk.LEFT, padx=5)
        
        self.stop_standalone_btn = ttk.Button(standalone_frame, text="免安装停止", command=self.stop_standalone)
        self.stop_standalone_btn.pack(side=tk.LEFT, padx=5)
        
        # 数据库导出选项
        export_frame = ttk.LabelFrame(main_frame, text="数据库导出选项", padding="10")
        export_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(export_frame, text="数据库名称:").grid(row=0, column=0, sticky=tk.W, pady=2)
        self.db_name_var = tk.StringVar()
        self.db_name_entry = ttk.Entry(export_frame, textvariable=self.db_name_var, width=30)
        self.db_name_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=2, padx=5)
        
        ttk.Label(export_frame, text="留空将导出所有数据库").grid(row=0, column=2, sticky=tk.W, pady=2, padx=5)
        
        ttk.Label(export_frame, text="输出文件:").grid(row=1, column=0, sticky=tk.W, pady=2)
        self.output_file_var = tk.StringVar()
        self.output_file_entry = ttk.Entry(export_frame, textvariable=self.output_file_var, width=50)
        self.output_file_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), pady=2, padx=5)
        
        browse_output_btn = ttk.Button(export_frame, text="浏览...", command=self.browse_output_file)
        browse_output_btn.grid(row=1, column=2, pady=2, padx=5)
        
        export_frame.columnconfigure(1, weight=1)
        
        # 日志区域
        log_frame = ttk.LabelFrame(main_frame, text="操作日志", padding="10")
        log_frame.pack(fill=tk.BOTH, expand=True, pady=5)
        
        self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, width=70, height=10)
        self.log_text.pack(fill=tk.BOTH, expand=True)
        self.log_text.config(state=tk.DISABLED)
    
    def update_status(self):
        """更新服务状态"""
        status = self.manager.get_service_status()
        self.status_var.set(f"服务状态: {status}")
        
        # 根据状态启用/禁用按钮
        if "运行中" in status:
            self.start_btn.config(state=tk.DISABLED)
            self.stop_btn.config(state=tk.NORMAL)
            self.export_btn.config(state=tk.NORMAL)
        elif status == "已停止":
            self.start_btn.config(state=tk.NORMAL)
            self.stop_btn.config(state=tk.DISABLED)
            self.export_btn.config(state=tk.DISABLED)
        else:  # 未安装或未知状态
            self.start_btn.config(state=tk.DISABLED)
            self.stop_btn.config(state=tk.DISABLED)
            self.export_btn.config(state=tk.DISABLED)
            
        # 免安装模式按钮状态
        if self.manager.mysql_process and self.manager.mysql_process.poll() is None:
            self.start_standalone_btn.config(state=tk.DISABLED)
            self.stop_standalone_btn.config(state=tk.NORMAL)
        else:
            self.start_standalone_btn.config(state=tk.NORMAL)
            self.stop_standalone_btn.config(state=tk.DISABLED)
    
    def log(self, message):
        """添加日志信息"""
        self.log_text.config(state=tk.NORMAL)
        timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")
        self.log_text.see(tk.END)
        self.log_text.config(state=tk.DISABLED)
    
    def browse_path(self):
        """浏览 MySQL 安装路径"""
        path = filedialog.askdirectory(title="选择 MySQL 安装路径")
        if path:
            self.path_var.set(path)
            self.manager.mysql_path = path
            self.manager.update_paths()
    
    def save_config(self):
        """保存配置"""
        self.manager.mysql_path = self.path_var.get()
        self.manager.mysql_service_name = self.service_var.get()
        self.manager.update_paths()
        self.manager.save_config()
        self.log(f"配置已保存: {self.manager.mysql_path}")
        messagebox.showinfo("成功", "配置已保存")
    
    def start_service(self):
        """启动服务"""
        result, message = self.manager.start_service()
        self.log(message)
        if result:
            messagebox.showinfo("成功", message)
        else:
            messagebox.showerror("错误", message)
        self.update_status()
    
    def stop_service(self):
        """停止服务"""
        result, message = self.manager.stop_service()
        self.log(message)
        if result:
            messagebox.showinfo("成功", message)
        else:
            messagebox.showerror("错误", message)
        self.update_status()
    
    def install_service(self):
        """安装服务"""
        if not self.manager.mysql_path:
            messagebox.showerror("错误", "请先配置 MySQL 安装路径")
            return
        
        if messagebox.askyesno("确认", "确定要安装并初始化 MySQL 服务吗？这可能会覆盖现有数据。"):
            result, message = self.manager.install_service()
            self.log(message)
            if result:
                messagebox.showinfo("成功", message)
                self.update_status()
            else:
                messagebox.showerror("错误", message)
    
    def uninstall_service(self):
        """卸载服务"""
        if not self.manager.is_service_installed():
            messagebox.showerror("错误", "MySQL 服务未安装")
            return
        
        if messagebox.askyesno("确认", "确定要卸载 MySQL 服务吗？数据不会被删除，但服务将无法启动。"):
            result, message = self.manager.uninstall_service()
            self.log(message)
            if result:
                messagebox.showinfo("成功", message)
                self.update_status()
            else:
                messagebox.showerror("错误", message)
    
    def browse_output_file(self):
        """浏览输出文件路径"""
        filename = filedialog.asksaveasfilename(
            title="选择导出文件位置",
            defaultextension=".sql",
            filetypes=[("SQL 文件", "*.sql"), ("所有文件", "*.*")]
        )
        if filename:
            self.output_file_var.set(filename)
    
    def export_database(self):
        """导出数据库"""
        db_name = self.db_name_var.get()
        output_file = self.output_file_var.get()
        
        if not output_file:
            messagebox.showerror("错误", "请指定输出文件")
            return
        
        result, message = self.manager.export_database(db_name, output_file)
        self.log(message)
        if result:
            messagebox.showinfo("成功", message)
        else:
            messagebox.showerror("错误", message)
    
    def start_standalone(self):
        """免安装启动 MySQL"""
        if not self.manager.mysql_path:
            messagebox.showerror("错误", "请先配置 MySQL 安装路径")
            return
        
        result, message = self.manager.start_standalone()
        self.log(message)
        if result:
            messagebox.showinfo("成功", message)
            self.update_status()
        else:
            messagebox.showerror("错误", message)
    
    def stop_standalone(self):
        """免安装停止 MySQL"""
        result, message = self.manager.stop_standalone()
        self.log(message)
        if result:
            messagebox.showinfo("成功", message)
            self.update_status()
        else:
            messagebox.showerror("错误", message)

if __name__ == "__main__":
    root = tk.Tk()
    app = MySQLManagerGUI(root)
    root.mainloop()    